unit GLGUITextComponent;

interface

uses
  Classes,
  GLContext,
  GLTextureFormat,
  OpenGLTokens,
  OpenGLAdapter,
  VectorGeometry,
  GLSGenerics,
  GLTexture,
  GLS_Material,
  GLGUIFonts,
  Dialogs,
  GLGUI2DSprite,
  GLGUITokens,
  GLState,
  GLGUIEvent,
  GLGUIEventManager;

type
  TGLGUITextComponent = class;

  TGLGUICharQuad = class
  protected
    FTexture: TGLTextureImageEx;
    FInfo: TGLGUIGlyphInfo;
    FMatrix: TMatrix;
    FParent: TGLGUITextComponent;
    FX: Single;
    FY: Single;
    FNeedCalcMatrix: Boolean;
    function GetMatrix(): TMatrix;
    procedure SetX(AX: Single);
    procedure SetY(AY: Single);
  public
    constructor Create(AParent: TGLGUITextComponent);
    property Info: TGLGUIGlyphInfo read FInfo write FInfo;
    property Matrix: TMatrix read GetMatrix;
    property Texture: TGLTextureImageEx read FTexture write FTexture;
    property X: Single read FX write FX;
    property Y: Single read FY write FY;
  end;

  TGLGUICharQuadsList = GList<TGLGUICharQuad>;

  TGLGUITextComponent = class(TGLGUI2DSpriteCustom)
  protected
    FFont: TGLGUIBitmapFont;
    FTexture: TGLTextureImageEx;
    FText: String;
    FGlyphs: TGLGUICharQuadsList;
    FLetterSpacing: Single;
    FUpdateTextOnFontChange: Boolean;
    function GetFontTexture(): TGLTextureImageEx; virtual;
    function GetText(): String; virtual;
    procedure SetText(const AText: String); virtual;
    function GetFont: TGLGUIBitmapFont; virtual;
    procedure SetFont(const AFont: TGLGUIBitmapFont); virtual;
    function GetTextHeight(): Single; virtual;
    function GetTextWidth(): Single; virtual;
    function GetTextLength(): Integer; virtual;
    function GetLetterSpacing: Single; virtual;
    procedure SetLetterSpacing(ALetterSpacing: Single); virtual;
    procedure OnFontUpdated(Aevent: TGLGUIEvent); virtual;
    function AddChar(const AChar: WideChar): TGLGUICharQuad; virtual; abstract;
  public
    constructor Create(AOwner: TComponent); override;
    procedure NotifyGUIChange(Sender: TObject = nil); override;
    function CharAtPosition(X, Y: Single) : TGLGUICharQuad; virtual;
    destructor Destroy; override;
    property Texture: TGLTextureImageEx read GetFontTexture;
    property Font: TGLGUIBitmapFont read GetFont write SetFont;
    property Text: String read GetText write SetText;
    property LetterSpacing: Single read GetLetterSpacing write SetLetterSpacing;
    property TextWidth: Single read GetTextWidth;
    property TextHeight: Single read GetTextHeight;
    property TextLength: Integer read GetTextLength;
  end;

implementation

constructor TGLGUICharQuad.Create(AParent: TGLGUITextComponent);
begin
  FParent := AParent;
  FNeedCalcMatrix := True;
end;

procedure TGLGUICharQuad.SetX(AX: Single);
begin
  FX := AX;
  FNeedCalcMatrix := True;
end;

procedure TGLGUICharQuad.SetY(AY: Single);
begin
  FY := AY;
  FNeedCalcMatrix := True;
end;

function TGLGUICharQuad.GetMatrix(): TMatrix;
var
  vTranslationMatrix,
  vScaleMatrix: TMatrix;
  vPosition: TVector;
  RC: TGLContext;
  vViewWidth, vViewHeight: Single;
begin
  if FNeedCalcMatrix then
  begin
    vPosition := VectorMake(FX, -FY, 0);
    vTranslationMatrix := CreateTranslationMatrix(vPosition);
    vScaleMatrix := CreateScaleMatrix(VectorMake(FInfo.Width * FParent.Scale.X,
      FInfo.Height * FParent.Scale.Y, 1));
    Result := vScaleMatrix;
    Result := MatrixMultiply(Result, vTranslationMatrix);
    Result := MatrixMultiply(Result, FParent.GetAbsoluteMatrixUnScaled);
    FMatrix := Result;
    FNeedCalcMatrix := False;
  end
  else
    Result := FMatrix;

  RC := CurrentGLContext;
  if (RC = nil) then
    Exit;

  vViewWidth := RC.GLStates.ViewPort[2];
  vViewHeight := RC.GLStates.ViewPort[3];

  Result[3,0] := Result[3,0] - vViewWidth/2;
  Result[3,1] := Result[3,1] + vViewHeight/2;
  Result[0,0] := Result[0,0]*2/vViewWidth;
  Result[1,1] := Result[1,1]*2/vViewHeight;
  Result[2,2] := 1;
  Result[3,0] := Result[3,0]*2/vViewWidth;
  Result[3,1] := Result[3,1]*2/vViewHeight;
  Result[3,3] := 1;
end;

constructor TGLGUITextComponent.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FGlyphs := TGLGUICharQuadsList.Create;
  Font := GLGUIDefaultBitmapFont;
  FUpdateTextOnFontChange := True;
  EventBehaviour := ebPassEvent;
  GUIEventManager.GetOrCreateEvent('FontUpdated').IgnoreState := True;
  AddEventListener('FontUpdated', OnFontUpdated);
end;

procedure TGLGUITextComponent.OnFontUpdated(AEvent: TGLGUIEvent);
begin
  Text := FText;
end;

function TGLGUITextComponent.GetFontTexture(): TGLTextureImageEx;
begin
  Result := FTexture;
end;

function TGLGUITextComponent.GetText(): String;
begin
  Result := FText;
end;

procedure TGLGUITextComponent.SetText(const AText: String);
begin
  FText := AText;
end;

function TGLGUITextComponent.GetFont: TGLGUIBitmapFont;
begin
  Result := FFont;
end;

procedure TGLGUITextComponent.SetFont(const AFont: TGLGUIBitmapFont);
begin
  if (AFont <> FFont)  then
  begin
    if ASsigned(FFont) then
      FFont.UnRegisterUser(Self);
    FFont := AFont;
    FFont.RegisterUser(Self);
    FTexture := FFont.Texture;
    if FUpdateTextOnFontChange then
      Text := FText;
  end;
  DispatchEvent('FontUpdated');
end;

function TGLGUITextComponent.GetTextHeight(): Single;
begin
  if FGlyphs.Count > 0 then
    Result := FGlyphs.Items[0].Info.Height
  else
    Result := 0;
end;

function TGLGUITextComponent.GetTextWidth(): Single;
begin
  Result := 0;

  if FGlyphs.Count > 0 then
    Result := FGlyphs.Last.X - FGlyphs.First.X + FGlyphs.Last.Info.Width/2 +
      FGlyphs.First.Info.Width/2;
end;

function TGLGUITextComponent.GetTextLength(): Integer;
begin
  Result := FGlyphs.Count;
end;

function TGLGUITextComponent.GetLetterSpacing: Single;
begin
  Result := FLetterSpacing;
end;

procedure TGLGUITextComponent.SetLetterSpacing(ALetterSpacing: Single);
begin
  FLetterSpacing := ALetterSpacing;
end;

procedure TGLGUITextComponent.NotifyGUIChange(Sender: TObject);
var
  i: Integer;
begin
  inherited;
  for i := 0 to FGlyphs.Count - 1 do
    FGlyphs.Items[i].FNeedCalcMatrix := True;
end;

function TGLGUITextComponent.CharAtPosition(X, Y: Single) : TGLGUICharQuad;
begin
  Result := nil;
end;

destructor TGLGUITextComponent.Destroy;
begin
  FGlyphs.Free;
  inherited;
end;

end.
